#include "dht.h"
#include "hw/board.h"
#include "hw/timer.h"
#include "hw/exti.h"
#include "supervisor.h"
#include <avr/io.h>
#include <cassert>

using namespace actor;

auto constexpr get_self = []() { return sup.get_child<0>(); };

void DHTActor::initialize() {
  // Go into high impedence state to let pull-up raise data line level and
  // start the reading process.
  PORTD |= (1U << PD2);
  DDRD &= ~(1 << DDD2);
  subscribe(&DHTActor::on_request);
  Parent::initialize();
}

void DHTActor::advance_init() {
    /*
    When power is supplied to the sensor, do not send any
    instruction to the sensor in within one second in order to pass the unstable status.
    */
    add_event<rl::ctx::thread>(1500000,[](void *) {
        get_self()->Parent::advance_init();
    }, this);
}


void DHTActor::on_request(message::RequestHT &) {
  send<rl::ctx::thread, message::LogStatic>(0, uart_id,
                                            "[dht] initializing\r\n");
  PORTD &= ~(1U << PD2);
  DDRD |= (1 << DDD2);
  // Data Single-bus voltage level from high to low
  // and this process must take at least 18ms to ensure DHT’s detection of MCU's
  // signal
  add_event<rl::ctx::thread>(
      18000,
      [](void *data) { static_cast<DHTActor *>(data)->handshake_continue(); },
      this);
}

void DHTActor::handshake_continue() {
  send<rl::ctx::thread, message::LogStatic>(0, uart_id,
                                            "[dht] hs, sending high\r\n");
  PORTD |= (1U << PD2);
  DDRD &= ~(1 << DDD2);

  substate = Substate::hs_keep_high;
  hw::Exti::start([](bool value){
     get_self()->on_edge(value);
  });
  hw::Timer::start();
}

template<int SZ> inline void format(char* ptr, uint8_t v) {
    for (int i = 0; i < SZ; ++i) {
      auto r = v % 10;
      v /= 10;
      *ptr-- = r + '0';
    }
}


void DHTActor::on_edge(bool value) {
  auto passed = hw::Timer::read_reset();
#if 0
  ++times;
#endif

  auto done = model.toggle(passed);
  if (done) {

      auto opt = model::DHT11::construct(model);
#if 0
      static char data[] = "000 000 000 000 000 000\r\n";
      auto ptr = data;
      auto r = 0;
      for(int i = 0; i < 3; ++i) {
          auto t = r % 10;
          r = r / 10;
          ptr[2 - i] = t + '0';
      }
      ptr += 4;

      for(int j = 0; j < Model::Size; ++j) {
          r = model.data[j];
          for(int i = 0; i < 3; ++i) {
              auto t = r % 10;
              r = r / 10;
              ptr[2 - i] = t + '0';
          }
          ptr += 4;
      }

      send<rl::ctx::interrupt, message::LogStatic>(0, uart_id, data);
#endif
     if (!opt) {
         send<rl::ctx::interrupt, message::LogStatic>(0, uart_id,
                                                      "[dht] checksum error\r\n");
     } else {
         static constexpr uint_fast8_t BUFF_SZ = 23;
         static char data[BUFF_SZ] = {"t = 00.00, h = 00.00\r\n"};
         auto& val = opt.value();
         auto& t = val.temperature();
         format<2>(data + 5, t.integral);
         format<2>(data + 8, t.decimal);

         auto& h = val.humidity();
         format<2>(data + 16, h.integral);
         format<2>(data + 19, h.decimal);
         send<rl::ctx::interrupt, message::LogStatic>(0, uart_id, data);
         PORTD |= (1U << PD2);
         DDRD &= ~(1 << DDD2);
     }
  }
}
